iT邦幫忙

2026 iThome 鐵人賽

DAY 3
0
AI Engineering

Agent 開發, 從本地到雲端: AI 工程師的 K8s 筆記系列 第 3

Day 03|Container 對 AI 工程師到底解決了什麼:環境漂移、資源衝突、重啟

  • 分享至 

  • xImage
  •  

cover

POV:你的機器才剛剛被無預警重開,10 幾隻 bot 在 22 秒內自己回復,但 tmux session 掛了,裡面有紀錄你相關監控的討論跟工作。

昨天講了一隻 bot 長大到多個 bot 逐漸失控。今天改用我的雲端 VM 正在跑的設定檔,來回答更基本的一件事:在還沒碰 K8s 之前,container 到底替我擋掉了哪些事?又有哪些事它擋不住?

我們來看一個真的在跑的服務範例。這是其中之一 LINE bot docker compose:

services:
  lineoa-course:
    image: ghcr.io/openabdev/openab:0.9.0-beta.10-codex
    restart: unless-stopped
    env_file: [lineoa-course.env]
    volumes:
      - ./config.toml:/etc/openab/config.toml:ro
      - codex-home:/home/node/.codex
    ports:
      - "127.0.0.1:8092:8080"
    healthcheck:
      test: ["CMD-SHELL", "pgrep -x openab || exit 1"]
      interval: 30s
    cap_drop: [ALL]
  cloudflared:
    image: cloudflare/cloudflared:latest
    restart: unless-stopped
    command: tunnel --config /etc/cloudflared/config.yml run
    depends_on: [lineoa-course]

然後我們來逐一敘述痛點,還有可以怎麼透過 docker compose 解決。

痛點一:環境漂移 (Environment drift)

通常 AI Agent 專案會有很多依賴:RAG Inference Engine、Whisper STT Engine、FastChat/vLLM Engine,再加一個 Next.js 前端。以前用 venv、conda、nvm 管理引擎版本,但是 library、環境變數、~/.cache 底下的東西也都要考慮,這表示也常常會漏掉。

而在 image: …:0.9.0-beta.10-codex 直接解釋這個:bot 需要什麼,一開始會打包成 image tag容器的 root filesystem,如果跑起來之後,要另外裝/升級 libraries,在重新開啟之後,就又回復到原本的 image。所以要保留必要的套件與版本,就要在 image 相關的底下作設定。

另外還有個要分享的:目前 volumes 只掛載兩個部分,一份唯讀的 config、一個具名 volume 放 bot 自己的狀態。但是不會放上 source code、跟其他 bot 的共用資料,這隻 bot 一概看不到。這就是透過 container 能夠隔離出不同 bot 的好處

痛點二:port 與資源衝突

同一台機器上,我現在有兩個 qdrant 在跑(RAG 研究環境、LINEOA 研究環境),還有十幾個 web 服務。首先就會先需要解決 port 相撞的問題。

Container 把這件事收斂成設定檔裡的一行。"127.0.0.1:8092:8080" 這行有兩個意思:容器內固定聽 8080(程式不用改),對外是 8092;而且只綁定本機不直接對外。另一個研究環境訂定慣例:host 端一律用 5 開頭的五位數,54001:400156333:6333,方便區分不同目的的專案。

資源設定,docker compose 可以設 mem_limit。我的 RAG 研究環境有設:

  • 推論引擎 6 GiB
  • 向量資料庫 1 GiB
  • Reverse Proxy 128 MiB
docker inspect openab-lineoa-course \
  --format '{{.HostConfig.RestartPolicy.Name}} {{.HostConfig.Memory}}'
# unless-stopped 0
docker stats --no-stream --format '{{.Name}}\t{{.MemUsage}}'

特別看一下 0:表示...我的 bot 全部沒設記憶體上限。 原因是一開始覺得 bot 很省(實測多在個位數到數十 MiB),所以沒特定設定管理。通常這個狀況,最後還是會遇到問題,導致需要重新檢視資源配置這些設定。

還有一個參數:mem_limit 是理論「上限」,不是「保證」。設了 6 GiB,不代表作業系統會替它留 6 GiB;如果其他 Container 或 Process 把記憶體吃光,它一樣會被波及。

痛點三:重啟

以我的例子,開發機器是 Spot VM,隨時可能被 Terminate。就在昨天早上,又重開了一次。接著我用 docker inspect 看每個容器的 StartedAt所以 bot 都在 22 秒後回來。

docker inspect $(docker ps -q) \
  --format '{{.Name}} {{.State.StartedAt}}' | sort -k2

回頭看 docker compose: restart: unless-stopped:所以機器重開,Container 也會自動重開;再配上 healthcheckpgrep -x openabdocker ps 會直接告訴我哪隻是 healthy、哪隻只是活著。

回頭對照如果尚未 Containerize 的服務。同一台機器上,我的 tmux 工作區和 coding agent session 是靠自己寫的還原腳本,重開之後有一部分沒回來(細節留到 Day 18)。

然而,Container 還沒解決...

  • 跨機器。 所有服務都壓在同一台 VM 上,一樣是 SPOF。
  • 資源保證。 mem_limit 只是理論上限,不代表保證;我的 bot 連上限都沒設。
  • 服務之間的依賴。 depends_on 只管啟動順序;bot 起來但還沒 ready,tunnel 就已經在轉流量了。要等到 healthy 得另外寫 condition: service_healthy
  • 服務發現。 compose 有內部 DNS(rag-engine:4001 這種),但範圍預設只在同一台機器、同一個 compose 專案裡。

這四件事,就是 K8s 存在的理由。但我還是建議:先理解這篇 docker compose 簡單的範例,確認三個痛點真的被解決了,再去碰 K8s。

接著,明天會提到如果「沒設上限」的代價。

今日一句話

Container 幫你把「這個服務需要什麼」寫下來;K8s 幫你把「這些服務之間的關係」寫下來。今天我們先學會服務需要什麼。

延伸閱讀


上一篇
Day 02|Agent 不是一個程式,是一堆程式:從一隻 bot 到一個艦隊
下一篇
Day 04|為什麼 docker compose 還是不夠: 當環境 swap 見底的時候...
系列文
Agent 開發, 從本地到雲端: AI 工程師的 K8s 筆記4
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言